/**
 *  Copyright Matt Silbernagel 2010
 *  
 *	This file is part of MobilePrinting.
 *
 *	MobilePrinting is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  MobilePrinting is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MobilePrinting.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.uc.labs.MobilePrinting.GUI;

import java.awt.AWTException;
import java.awt.CheckboxMenuItem;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.TrayIcon.MessageType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import java.io.IOException;
import java.util.ArrayList;

import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import static edu.uc.labs.MobilePrinting.Configurator.*;

//import edu.uc.labs.MobilePrinting.Prefs.Preferences;
import edu.uc.labs.MobilePrinting.Configurator;
import edu.uc.labs.MobilePrinting.Prefs.Preferences;
import edu.uc.labs.MobilePrinting.PrintUtils.Printer;
import edu.uc.labs.MobilePrinting.PrintUtils.PrinterFinder;
import edu.uc.labs.MobilePrinting.PrintUtils.PrinterManager;
import edu.uc.labs.MobilePrinting.PrintUtils.PrinterWriter;

/* 
 * @author Matt Silbernagel
 */
public class SysTray extends Thread {

	private static TrayIcon trayIcon = null;
	private static String IMG;
	private static String ERRIMG;
	private static String PROGIMG;
	private ArrayList<Printer> printers = null;
	private PopupMenu popup = new PopupMenu();
	private PopupMenu updating = new PopupMenu();
	private SystemTray tray;
	private static Image image;
	private Preferences Prefs;

	/**
	 * Constructor for the SysTray class
	 */
	public SysTray() {

		try {
			// Set System L&F
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (UnsupportedLookAndFeelException e) {
			LOGGER.debug("UnsupportedLookAndFeelException: " + e.toString());
		} catch (ClassNotFoundException e) {
			LOGGER.debug("ClassNotFoundException: " + e.toString());
		} catch (InstantiationException e) {
			LOGGER.debug("InstantiationException: " + e.toString());
		} catch (IllegalAccessException e) {
			LOGGER.debug("IllegalAccessException: " + e.toString());
		}

		IMG = ICONS + DELIM + IconArray.get("trayicon_norm");;
		ERRIMG = ICONS + DELIM + IconArray.get("trayicon_error");
		PROGIMG = ICONS + DELIM + IconArray.get("trayicon_prog1");
	}

	/**
	 * Start the thread and main functionality
	 */
	public void run() {
		if (SystemTray.isSupported()) {
			tray = SystemTray.getSystemTray();
			image = Toolkit.getDefaultToolkit().getImage(IMG);
			
			MenuItem noPrinters = new MenuItem("No Printers Accessible");
			popup.add(noPrinters);
			popup.addSeparator();
			this.getDefaultSet();

			MenuItem nowInUpdate = new MenuItem("Updating");
			updating.add(nowInUpdate);
			updating.addSeparator();
			this.getDefaultSet();

			trayIcon = new TrayIcon(image, "Mobile Printing", popup);
			trayIcon.setImageAutoSize(true);
			
			//LOGGER.debug("Image size = " + trayIcon.getSize());

			try {
				tray.add(trayIcon);
			} catch (AWTException e) {
				LOGGER.error("TrayIcon could not be added. ");
			}

			// Now do the work
			ArrayList<Printer> list = PrinterFinder.getList();
			add(list);

		} else {
			LOGGER.debug("System Tray is not supported");
		}
	}

	/**
	 * Adds the specified list of printers to the system tray icon
	 * and installs the last used printer in the current zone
	 * @param ArrayList
	 *            <Printer>
	 */
	synchronized public void add(ArrayList<Printer> p) {
		printers = p;
		Printer cache = null;
		if (!printers.isEmpty()) {

			if ((getZone() != null) || (!getZone().isEmpty())) {
				LOGGER.debug("Currently in the " + getZone() + " zone.");
				try {
					cache = PrinterWriter.getCache(getZone());
					LOGGER.debug("the default printer should be "
							+ cache.getName());
				} catch (NullPointerException ex) {
				}
			} else {
				LOGGER.debug("SysTray: getZone() is null or an empty string");
			}
		}

		// Remove everything from the popup menu
		// so we can rebuild it.
		popup.removeAll();

		if (printers.isEmpty()) {
			MenuItem noPrinters = new MenuItem("No Printers Accessible");

			// TODO Un-install the current printer if there are no printers
			// accessible
			Prefs = Configurator.getPrefs();
			Printer cp = PrinterWriter.getCurrent();
			PrinterManager pm = new PrinterManager(cp);
			try {
				pm.remove(Prefs.getPrinterName());
			} catch (IOException e) {
				LOGGER.error("Couldn't remove the printer for some reason");
			}
			noPrinters.setEnabled(false);
			popup.add(noPrinters);
		} else {
			// LOGGER.debug("the arraylist \'printers\' doesn't seem to be empty: "
			// + printers.size() + " elements");

			// Loop through the list of available printers
			for (final Printer prn : printers) {
				final CheckboxMenuItem c = new CheckboxMenuItem(prn.getName());
				// LOGGER.debug(prn.toString());
				ItemListener prnItemListener = new ItemListener() {

					@Override
					public void itemStateChanged(ItemEvent e) {
						// Do the work as long as
						// this isn't the currently active
						// printer
						if (!prn.isActive()) {
							// Set all printers in the menu unactive
							setAllPrintersInactive();
							// Then set this one to active
							prn.setActive(true);
							c.setState(true);
							LOGGER.debug("Installing this printer:\n\n" + prn.toString());
							prn.installPrinter();
						} else {
							prn.setActive(true);
							c.setState(true);
						}
					}
				};

				c.addItemListener(prnItemListener);
				popup.add(c);
			}
		}

		popup.addSeparator();
		this.getDefaultSet();

		// Loop through the checklist
		// and install the default printer if
		// there is one
		if ((cache != null) && (!cache.isEmpty())) {
			// LOGGER.debug("Installing the cached printer for this zone: " +
			// cache.toString());
			// If there is a Printer in cache
			// then execute the loop
			for (int i = 0; i < popup.getItemCount(); i++) {
				try {
					CheckboxMenuItem mi = (CheckboxMenuItem) popup.getItem(i);
					if (mi.getName() != null) {
						if (mi.getLabel().equalsIgnoreCase(cache.getName())) {
							cache.installPrinter();
							cache.setActive(true);
							mi.setState(true);
						}
					}
				} catch (ClassCastException e) {
					// Not a check box, continue...
				}
			}
		}
	}

	/**
	 * Set the popup menu to have the default set of options
	 */
	private void getDefaultSet() {

		MenuItem refresh = new MenuItem("Refresh Printer List");
		MenuItem prefs = new MenuItem("Preferences...");
		MenuItem about = new MenuItem("About MobilePrinting...");
		MenuItem exit = new MenuItem("Exit");

		ActionListener exitListener = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// Get the user or default preferences
				Prefs = Configurator.getPrefs();
				if (Prefs.isDeleteOnExit()) {
					Printer cp = PrinterWriter.getCurrent();
					PrinterManager pm = new PrinterManager(cp);
					LOGGER.debug("Trying to delete the printer "
							+ Configurator.PRINTERNAME);
					try {
						pm.remove(Configurator.PRINTERNAME);
					} catch (IOException e1) {
						LOGGER
								.error("Couldn't remove the printer for some reason: "
										+ e1.toString());
					}
				}
				LOGGER.debug("Exiting application");
				PreferencesPanel.isOpen = false;
				System.exit(0);
			}
		};

		ActionListener aboutListener = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				LOGGER.debug("About Menu...");
				new AboutPanel("About MobilePrinting");
			}
		};

		ActionListener prefListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				LOGGER.debug("Opening Preferences Menu...");
				PreferencesPanel.createAndShowGUI();
			}
		};
		
		ActionListener refreshListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				LOGGER.debug("Refreshing printer list");
				ArrayList<Printer> list = PrinterFinder.getList();
				add(list);
			}
		};

		refresh.addActionListener(refreshListener);
		about.addActionListener(aboutListener);
		exit.addActionListener(exitListener);
		prefs.addActionListener(prefListener);

		popup.add(refresh);
		popup.addSeparator();
		popup.add(prefs);
		popup.addSeparator();
		popup.add(about);
		popup.add(exit);
	}

	/**
	 * Get all of the printers in the list and uncheck all of them
	 */
	synchronized public void setAllPrintersInactive() {

		for (int i = 0; i < popup.getItemCount(); i++) {
			try {
				CheckboxMenuItem mi = (CheckboxMenuItem) popup.getItem(i);
				mi.setState(false);
			} catch (ClassCastException e) {
			}
		}
		for (Printer p : printers) {
			p.setActive(false);
		}

	}

	/**
	 * Static method that sends a message to be displayed 
	 * by the current SysTray Object
	 * @param title The title of the message
	 * @param msg   The body of teh message
	 * @param type  One of MessageType.INFO MessageType.ERROR MessageType.NONE 
	 */
	public static void sendMessage(String title, String msg, MessageType type) {
		if (!OS.equalsIgnoreCase("Mac OS X")) {
			trayIcon.displayMessage(title, msg, type);
		}
	}
	
	/**
	 * Static method that replaces the icon currently displayed
	 * with the Image specified 
	 * @param icon the Image to display in the SysTray
	 */
	public static void setIcon(Image icon){
		image.flush();
		image = icon;
		trayIcon.setImage(image);
	}
	
	/**
	 * Static method that sets the icon displayed 
	 * to the default icon ( defined as trayicon_norm in Configurator )
	 */
	public static void setDefaultIcon(){
		image.flush();
		image = Toolkit.getDefaultToolkit().getImage(IMG);
		trayIcon.setImage(image);
	}
	
	/**
	 * Static method that sets the icon displayed
	 * to the error icon ( defined as trayicon_error in Configurator )
	 */
	public static void setErrorIcon() {
		image.flush();
		image = Toolkit.getDefaultToolkit().getImage(ERRIMG);
		trayIcon.setImage(image);
	}
	
	/**
	 * Static method that sets the icon displayed
	 * to the progress icon ( defined as trayicon_prog in Configurator )
	 */
	public static void setProgIcon() {
		image.flush();
		image = Toolkit.getDefaultToolkit().getImage(PROGIMG);
		trayIcon.setImage(image);
	}

}
